package util;

import controller.GameController;
import entities.BasePiece;
import listener.MessageListener;
import org.json.JSONArray;
import org.json.JSONObject;

import java.awt.*;
import java.io.*;
import java.net.Socket;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

/***
 * Author: Mr.Zhao, Time: 2018/5/5/0:48
 * Description: 客户端类，负责从服务器获取请求
 */
public class MyClient {
    /**
     * 用于解决多线程并发冲突问题
     * 参考资料：https://blog.csdn.net/lufeng20/article/details/24314381
     */
    private static final ThreadLocal<Socket> threadConnect = new ThreadLocal<Socket>();
    private static Socket socket;

    /* 与服务器建立输出流 */
    private static OutputStream outStr = null;
    /* 向服务器写数据 */
    private static PrintWriter outWriter = null;
    //private BufferedWriter outWriter=null;

    /* 从服务器获取到的输入流 */
    private static InputStream inStr = null;
    /* 从服务器读取数据 */
    private static InputStreamReader inReader = null;

    /* 接收数据的线程 */
    public Thread tRecv = new Thread(new RecieveThread());

    /* 保持一直在线状态的线程 */
    private Thread tKeep = new Thread(new KeepThread());

    /* 对手玩家的客户端标志 */
    private int opNum = 0;

    private GameController gameController = null;
    /* 是否已与服务器建立连接 */
    private boolean isConnected = false;
    /* 单例模式 */
    private static MyClient instance = null;

    //private Set<MessageListener> listeners = new HashSet<MessageListener>();
    //线程安全对象，不会导致在遍历时添加监听器产生ConcurrentModificationException并发异常
    private Set<MessageListener> listeners = new CopyOnWriteArraySet<>();

    private MyClient() {

    }

    public static MyClient getInstance() {
        if (instance == null) {
            instance = new MyClient();
        }
        return instance;
    }


    public void connect() {
        System.out.println("======开始与服务器建立连接======");
        socket = threadConnect.get();
        try {
            if (socket == null) {
                socket = new Socket(MessageListener.REMOTE_IP, MessageListener.REMOTE_PORT);
                threadConnect.set(socket);

                outStr = socket.getOutputStream();
                outWriter = new PrintWriter(new OutputStreamWriter(outStr, "UTF-8"),
                        true);
                //outWriter=new BufferedWriter(new OutputStreamWriter(outStr,"UTF-8"));

                inStr = socket.getInputStream();
                inReader = new InputStreamReader(inStr, "UTF-8");

                /* 启动发送心跳包线程 */
                tKeep.start();
                /* 启动接收数据线程 */
                tRecv.start();
            }
        } catch (IOException e) {
            //e.printStackTrace();
            isConnected = false;
            System.out.println("服务器连接失败");
//            tKeep.interrupt();
//            tRecv.interrupt();
        }


    }

    /**
     * 每隔一秒向服务器发送一个心跳包表示自己在线
     */
    private class KeepThread implements Runnable {
        public void run() {
            System.out.println("======开始发送心跳包======");
            while (true) {
                try {
                    Thread.sleep(1000);
                    outWriter.println(MessageListener.HEARTBEAT);
                    isConnected = true;
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                    isConnected = false;
                    break;
                }
            }
        }
    }

    /**
     * 不断从服务器获取数据
     */
    private class RecieveThread implements Runnable {
        public void run() {
            System.out.println("=====开始接收数据=====");
            while (true) {
                try {
                    BufferedReader bfr = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                    String str;
                    while ((str = bfr.readLine()) != null) {
                        System.out.println("从服务端获取到的消息为：" + str);
                        dispatchMessage(str);
                    }
                } catch (IOException e) {
                    System.out.println("服务器掉线");
                    isConnected = false;
                    tKeep.interrupt();
                    tRecv.interrupt();
                    break;
                }
            }
        }
    }

    public void sendMessage(String msg) {
        outWriter.println(msg);
    }

    /**
     * 将收到的消息进行分发
     *
     * @param jsonStr json数据
     */
    private void dispatchMessage(String jsonStr) {
        JSONObject jsonObject = new JSONObject(jsonStr);
        String flag = jsonObject.getString("flag");

        if (flag.equals("我的信息")) {
            JSONObject myInfoJson = jsonObject.getJSONObject("myInfo");
            String id = myInfoJson.getString("id");
            for (MessageListener l : listeners) {
                l.myInfo(id);
            }
        }

        if (flag.equals("收到游戏邀请")) {
            JSONObject playerInfoJson = jsonObject.getJSONObject("playerInfo");
            String id = playerInfoJson.getString("id");
            for (MessageListener l : listeners) {
                l.receiveInvitation(id);
            }
        }

        if (flag.equals("游戏开始")) {
            JSONObject myInfo = jsonObject.getJSONObject("myInfo");
            String colorName = myInfo.getString("color");
            Color color = colorName.equals("黑色") ? Color.BLACK : Color.RED;
            for (MessageListener l : listeners) {
                l.gameStart(color);
            }
        }

        if (flag.equals("拒绝游戏邀请")) {
            for (MessageListener l : listeners) {
                l.refusedInvitation();
            }
        }

        if (flag.equals("走棋")) {
            for (MessageListener l : listeners) {
                l.opponentMoved(jsonObject.getInt("x1"), jsonObject.getInt("y1"),
                        jsonObject.getInt("x2"), jsonObject.getInt("y2"));
            }
        }

        if (flag.equals("聊天")) {
            String time = jsonObject.getString("time");
            String content = jsonObject.getString("content");
            for (MessageListener l : listeners) {
                l.receivedChatMsg(time, content);
            }
        }

        if (flag.equals("在线玩家列表")) {
            JSONArray jsonArray = jsonObject.getJSONArray("onlinePlayers");
            for (MessageListener l : listeners) {
                l.listPlayers(jsonArray.toString());
            }
        }

        if (flag.equals("对手离线")) {
            for (MessageListener l : listeners) {
                l.opponentOffline();
            }
        }
    }

    public synchronized void addMessageListener(MessageListener l) {
        if (l == null) {
            return;
        }
        this.listeners.add(l);
    }

    public synchronized void removeMessageListener(MessageListener l) {
        if (l == null) {
            return;
        }
        this.listeners.remove(l);
    }

    public int getOpNum() {
        return opNum;
    }

}
